`timescale 1ns / 1ps
/************************************************************\
 **  Copyright (c) 2022-2023 Gonsin, Inc.
 **  All Right Reserved.
 **  Author: http://www.anlogic.com/
 **  Description: rgmii_interface
 **  Rev 1.0
\************************************************************/

module rgmii_interface (
// 同步复位
input tx_reset_i,
input rx_reset_i,

// 指示当前运行速度为10/100
input speed10_100_i,

// 以下端口是RGMII物理接口：这些端口将位于FPGA的引脚上
output [3:0] rgmii4b_txd_o,
output rgmii_txCtl_o,
output rgmii_txc_o,
input [3:0] rgmii4b_rxd_i,
input rgmii_rxCtl_i,
input rgmii_rxc_i,

// 以下信号为RGMII状态信号
output reg link_status_o,
output reg [1:0] clock2b_speed_o,
output reg duplex_status_o,

// 以下端口连接到 TEMAC核 的 内部GMII接口模块
//input gmii_txcen_i,
input [7:0] gmii8b_txd_i,
input gmii_txen_i,
input gmii_txer_i,
output gmii_crs_o,
output gmii_col_o,
output [7:0] gmii8b_rxd_o,
output gmii_rxdv_o,
output gmii_rxer_o,

//发送时钟信号
input tx_clk_i,                // gtx_clk: 125MHz
input rgmii_txcen_i,
input rgmii_txcen_shift_i,

// MAC核 和 客户端逻辑 的接收器时钟信号
output rx_rgmii_clk_o       //output：125MHz（1Gbps） 25MHz(100Mbps)  2.5MHz(10Mbps)
);

//----------------------------------------------------------------------------
// 模块 内部 信号
//----------------------------------------------------------------------------
reg [3:0] gmii4b_txd_falling;       // gmii_txd信号在tx_clk的下降沿锁存。
wire rgmii_txc_odelay_w;            // RGMII接收器时钟ODDR输出.
wire rgmii_txctl_odelay_w;          // RGMII控制信号ODDR输出.
wire [3:0] rgmii4b_txd_odelay_w;    // RGMII数据ODDR输出.
wire rgmii_txctl_int_w;             // 内部RGMII传输控制信号.
wire rgmii_rxctl_delay_w;
wire [3:0] rgmii4b_rxd_delay_w;
wire rgmii_rxCtl_w;                 // 内部RGMII接收器控制信号.

reg tx_en_toddr;

wire gmii_rxdv_w;               
wire gmii_rxer_w;               
wire [7:0] gmii8b_rxd_w;                 

wire inband_ce_w;                    //RGMII带内状态寄存器 使能输出信号
wire rgmii_rxc_int_w;

//==============================================================================
// RGMII 发送逻辑
//==============================================================================

//----------------------------------------------------------------------------
// RGMII 发送器时钟管理：rgmii_txc
//----------------------------------------------------------------------------
// 产生 rgmii_txc_o 时钟.
oddr rgmii_txc_ddr (
.q(rgmii_txc_odelay_w),   //output：125MHz（1Gbps） 25MHz(100Mbps)  2.5MHz(10Mbps)
.clk(tx_clk_i),
.d1(rgmii_txcen_i),
.d0(rgmii_txcen_shift_i),
.rst(tx_reset_i)
);

// 延时输出2ns,以便将时钟边沿集中在rgmii_txd [3：0]有效窗口内。仿真0.285ns
// EG_LOGIC_ODELAY #(
// .OUTDEL(1)
// ) delay_rgmii_tx_clk (
// .i(rgmii_txc_odelay_w),
// .o(rgmii_txc_o)        //output：125MHz（1Gbps） 25MHz(100Mbps)  2.5MHz(10Mbps)
// );

assign rgmii_txc_o = rgmii_txc_odelay_w;
   
//---------------------------------------------------------------------------
// RGMII 发送逻辑 : rgmii4b_txd_o
//---------------------------------------------------------------------------
// 1Gbps gmii8b_txd_i  8位有效; rgmii_txc双沿使能发送8位
// 10/100Mbps gmii8b_txd_i 仅低四位有效，rgmii_txc双沿使能重复发送低四位
// 1Gbps时，125mhz的rgmii_txc，上升沿发送低四位，下降沿发送高四位。 一个时钟周期8bit, 125*8=1Gbps
// 100Mbps时，25mhz的rgmii_txc，上升沿发送低四位，下降沿发送低四位。 相当于一个时钟周期只发一个4bit, 25*4=100mbps
// 10Mbps时同100mbps,数据有效位在每byte的低四位，虽然一个时钟周期重复发低四位，相当于一个时钟周期只发一个低4位。

always@(speed10_100_i, gmii8b_txd_i) begin
  if (speed10_100_i == 1'b0) // 1Gbps gmii8b_txd_i  8位有效
    gmii4b_txd_falling <= gmii8b_txd_i[7:4];
  else      // 10/100Mbps gmii_txd高四位无效，rgmii_txc双沿使能发送低四位
    gmii4b_txd_falling <= gmii8b_txd_i[3:0];
end

genvar i;
generate for(i=0; i<4; i=i+1)
  begin : txdata_out_bus

  oddr rgmii_txd_out (
  .q(rgmii4b_txd_odelay_w[i]),
  .clk(tx_clk_i),
  .d1(gmii4b_txd_falling[i]),
  .d0(gmii8b_txd_i[i]),
  .rst(tx_reset_i)
  );

  //延迟输出 2 ns； 仿真：0.175ns
  //EG_LOGIC_ODELAY #(
  //.OUTDEL(0)
  //) delay_rgmii_txd (
  //.i(rgmii4b_txd_odelay_w[i]),
  //.o(rgmii4b_txd_o[i])
  //);
  assign rgmii4b_txd_o[i] = rgmii4b_txd_odelay_w[i];

  end
endgenerate

//---------------------------------------------------------------------------
// RGMII 发送逻辑 : rgmii_txCtl_o
//---------------------------------------------------------------------------
// 编码 rgmii ctl 信号
assign rgmii_txctl_int_w = gmii_txen_i ^ gmii_txer_i;

// 需要逻辑以确保 错误信号 将在整个时钟相位内，将tx_ctl发送为低电平
always@(speed10_100_i or gmii_txen_i or gmii_txer_i or rgmii_txcen_i) begin
  if(speed10_100_i)
    tx_en_toddr = gmii_txen_i & (!gmii_txer_i || rgmii_txcen_i);
  else
    tx_en_toddr = gmii_txen_i;
end

// oDDR primitive
oddr ctl_output (
.q(rgmii_txctl_odelay_w),
.clk(tx_clk_i),
.d1(rgmii_txctl_int_w),
.d0(tx_en_toddr),
.rst(tx_reset_i)
);

// 延迟输出 2 ns； 仿真：0.175ns
//EG_LOGIC_ODELAY #(
//.OUTDEL(0)
//) delay_rgmii_tx_ctl (
//.i(rgmii_txctl_odelay_w),
//.o(rgmii_txCtl_o)
//);
assign rgmii_txCtl_o = rgmii_txctl_odelay_w;

//==============================================================================
// RGMII 接收逻辑
//==============================================================================

//---------------------------------------------------------------------------
// RGMII 接收逻辑：rgmii_rxc
//---------------------------------------------------------------------------

assign rgmii_rxc_int_w = rgmii_rxc_i;      // input 125MHz（1Gbps） 25MHz(100Mbps)  2.5MHz(10Mbps)
assign rx_rgmii_clk_o  = rgmii_rxc_int_w;  // 内部信号给到输出端口
   
//---------------------------------------------------------------------------
// RGMII 接收逻辑：rgmii_rxd ---->  gmii8b_rxd_w
//---------------------------------------------------------------------------
// 1Gbps gmii8b_rxd_o  8位有效; rgmii_rxc双沿使能接收8位
// 10/100Mbps gmii8b_rxd_o 仅低四位有效，rgmii_rxc双沿使能接收高低四位数据相同
// 1gbps时，125mhz的rgmii_rxc，上升沿接收四位数据（对应低4bit），下降沿接收四位数据（对应高4bit）。 一个时钟周期8bit, 125mhz*8=1gbps
// 100mbps时，25mhz的rgmii_rxc，上升沿接收四位数据，下降沿接收四位数据（高四位和低四位数据相同）。 相当于单沿采样，一个时钟周期只收一个4bit（数据有效位在每byte的低四位：高低四位重复）, 25mhz*4=100mbps
// 10mbps时同100mbps,虽然一个时钟周期上下沿重复接收相同四位数据，相当于一个时钟周期只收一个4bit。

genvar j;
generate for (j=0; j<4; j=j+1)
  begin : rxdata_bus

  //EG_LOGIC_IDELAY delay_rgmii_rxd (
  //.i(rgmii4b_rxd_i[j]),
  //.o(rgmii4b_rxd_delay_w[j])
  //);
  assign rgmii4b_rxd_delay_w[j] = rgmii4b_rxd_i[j];

  end
endgenerate

// Instantiate Double Data Rate Input flip-flops.
// DDR_CLK_EDGE attribute specifies output data alignment from IDDR component

genvar k;
generate for(k=0; k<4; k=k+1)
  begin : rxdata_in_bus

  iddr rgmii_rx_data_in (
  .q1(gmii8b_rxd_w[k+4]),
  .q0(gmii8b_rxd_w[k]),
  .clk(rgmii_rxc_int_w),
  .d(rgmii4b_rxd_delay_w[k]),
  .rst(1'b0)
  );

  end
endgenerate
   
//---------------------------------------------------------------------------
// RGMII 接收逻辑：rgmii_rx_ctl ------> gmii_rx_dv、gmii_rx_er
//---------------------------------------------------------------------------

//EG_LOGIC_IDELAY delay_rgmii_rx_ctl (
//.i(rgmii_rxCtl_i),
//.o(rgmii_rxctl_delay_w)
//);
assign rgmii_rxctl_delay_w = rgmii_rxCtl_i;

iddr rgmii_rx_ctl_in (
.q1(rgmii_rxCtl_w),
.q0(gmii_rxdv_w),
.clk(rgmii_rxc_int_w),
.d(rgmii_rxctl_delay_w),
.rst(1'b0)
);

// 解码 gmii_rxer_o signal
assign gmii_rxer_w = gmii_rxdv_w ^ rgmii_rxCtl_w;

//----------------------------------------------------------------------------
// 接收逻辑：内部信号给到输出端口：gmii_rxd、gmii_rx_dv、gmii_rx_er、gmii_col、gmii_crs
//----------------------------------------------------------------------------
assign gmii8b_rxd_o = gmii8b_rxd_w;
assign gmii_rxdv_o = gmii_rxdv_w;
assign gmii_rxer_o = gmii_rxer_w;

// 从RGMII创建GMII格式的冲突和载波侦听信号 
assign gmii_col_o = (gmii_txen_i | gmii_txer_i) & (gmii_rxdv_w | gmii_rxer_w);
assign gmii_crs_o = (gmii_txen_i | gmii_txer_i) | (gmii_rxdv_w | gmii_rxer_w);

//==============================================================================
// RGMII 状态寄存器
//==============================================================================

// 在帧间间隔期间启用带内状态寄存器

assign inband_ce_w = !(gmii_rxdv_w || gmii_rxer_w);

always@(posedge rgmii_rxc_int_w) begin
  if(rx_reset_i) begin
    link_status_o <= 1'b0;
    clock2b_speed_o[1:0] <= 2'b0;
    duplex_status_o <= 1'b0;
  end
  else if(inband_ce_w) begin
    link_status_o <= gmii8b_rxd_w[0];
    clock2b_speed_o[1:0] <= gmii8b_rxd_w[2:1];
    duplex_status_o <= gmii8b_rxd_w[3];
  end
end

endmodule

